<?php
declare(strict_types=1);
namespace Haozing\Core\Service\Model;

use Haozing\Core\Constants\ErrorCode;
use Hyperf\Database\Model\Concerns\CamelCase;
use Hyperf\Database\Model\SoftDeletes;
use Hyperf\Stringable\Str;
use function Hyperf\Config\config;

abstract class AbstractModel extends Model
{
    use CamelCase;
    use SoftDeletes;

    /**
     * 查询单条 - 根据ID
     * @param int $id ID
     * @param array $fields 字段
     * @return array
     */
    public function getOneById(int $id, array $fields = ['*']): array
    {
        $data = self::query()->find($id, $fields);
        //如果$data不为空，$data转为数组，否则返回空数组
        return $data? $data->toArray() : [];
    }

    /**
     * 查询单条 - 根据条件
     * @param array $where 条件
     * @param array $fields 字段
     * @return array
     */
    public function getOneByWhere(array $where, array $fields = ['*'],$options = []): array
    {
        $model = $this->optionWhere($where, $options);
        $data = $model->first($fields);
        //如果$data不为空，$data转为数组，否则返回空数组
        return $data? $data->toArray() : [];
    }

    /**
     * 查询多条 - 根据条件
     * @param array $where 条件
     * @param array $fields 字段
     * @param int $limit 条数
     * @return array
     */
    public function getListByWhere(array $where, array $fields = ['*'] ,$options = [], int $limit = 1000): array
    {
        $model = $this->optionWhere($where, $options);
        $data = $model->limit($limit)->get($fields);
        //如果$data不为空，$data转为数组，否则返回空数组
        return $data? $data->toArray() : [];
    }

    /**
     * 查询多条 - 根据ID
     * @param array $ids ID
     * @param array $fields 字段
     * @return array
     */
    public function getListByWhereIn(array $ids, array $fields = ['*']): array
    {
        $data = self::query()->whereIn('id', $ids)->get($fields);
        //如果$data不为空，$data转为数组，否则返回空数组
        return $data? $data->toArray() : [];
    }

    /**
     * 查询列表 - 根据条件
     * @param array $where 条件
     * @param array $fields 字段
     * @param string $order 排序
     * @param int $page 页码
     * @param int $limit 条数
     * @return array
     */
    public function getPageByWhere(array $where, array $fields = ['*'], $options=[]): array
    {
        $model = $this->optionWhere($where, $options);


        ## 分页参数
        $perPage  = isset($options['pageSize']) ? (int) $options['pageSize'] : 15;
        $pageName = $options['pageName'] ?? 'pageNo';
        $page     = isset($options['pageNo']) ? (int) $options['pageNo'] : null;

        ## 分页
        $data          = $model->paginate($perPage, $fields, $pageName, $page);
        $data || $data = \Hyperf\Collection\collect([]);
        return $data->toArray();
    }

    /**
     * 添加单条
     * @param array $data 数据
     * @return int 自增ID
     */
    public function addOne(array $data): int
    {
        return self::query()->insertGetId($this->columnsFormat($data));
    }

    /**
     * 添加多条
     * @param array $data 数据
     * @return bool 是否成功
     */
    public function addAll(array $data): bool
    {
        //字段过滤
        foreach ($data as $key => $value) {
            $data[$key] = $this->columnsFormat($value);
        }
        return self::query()->insert($data);
    }

    /**
     * 更新单条 - 根据ID
     * @param array $data 数据
     * @return int 影响行数
     */
    public function updateOneById(int $id, array $data): int
    {
        return self::query()->where('id', $id)->update($this->columnsFormat($data));
    }

    /**
     * 更新单条 - 根据条件
     * @param array $where 条件
     * @param array $data 数据
     * @param array $options
     * @return int 影响行数
     * @throws \Exception
     */
    public function updateByWhere(array $where, array $data,array $options=[]): int
    {
        //字段过滤
        $model = $this->optionWhere($where, $options);
        $data = $model->update($this->columnsFormat($data));
        if (!$data){
            throw new \Exception("更新失败",ErrorCode::BAD_REQUEST);
        }
        return $data;
    }

    /**
     * 删除单条 - 根据ID
     * @param int $id ID
     * @return int 影响行数
     */
    public function deleteOneById(int $id): int
    {
        return self::query()->destroy($id);
    }

    /**
     * 删除 - 根据条件
     * @param array $where 条件
     * @return int 影响行数
     */
    public function deleteByWhere(array $where,array $options =[]): int
    {
        try {
            $model = $this->optionWhere($where, $options);
            return $model->delete();
        } catch (\Exception $e) {
            return 0;
        }
    }

    /**
     * 删除多条 - 根据ID
     * @param array $ids ID
     * @return int 影响行数
     */
    public function deleteAllById(array $ids): int
    {
        return self::query()->destroy($ids);
    }

    /**
     * 批量修改 - case...then...根据ID.
     * @param array $values 修改数据(必须包含ID)
     * @param bool $transToSnake 是否key转snake
     * @param bool $isColumnFilter 是否过滤不存在于表内的字段数据
     * @return int 影响条数
     */
    public function batchUpdateByIds(array $values, bool $transToSnake = false, bool $isColumnFilter = false): int
    {
        ## ksort
        foreach ($values as &$value) {
            ksort($value);
            $transToSnake && $value = $this->columnsFormat($value, $transToSnake, $isColumnFilter);
        }

        //获取数据库配置
        $config           = config('database');
        var_dump($config);
        $tablePrefix      = \Hyperf\DbConnection\Db::connection()->getTablePrefix();
        $table            = $this->getTable();
        $primary          = $this->getKeyName();
        [$sql, $bindings] = $this->compileBatchUpdateByIds($tablePrefix . $table, $values, $primary);

        return \Hyperf\DbConnection\Db::update($sql, $bindings);
    }

    /**
     * Compile batch update Sql.
     * @param string $table ...
     * @param array $values ...
     * @param string $primary ...
     * @return array update sql,bindings
     */
    protected function compileBatchUpdateByIds(string $table, array $values, string $primary): array
    {
        if (! is_array(reset($values))) {
            $values = [$values];
        }

        // Take the first value as columns
        $columns  = array_keys(current($values));
        // values
        $bindings = [];

        $setStr = '';
        foreach ($columns as $column) {
            if ($column === $primary) {
                continue;
            }

            $setStr .= " `{$column}` = case `{$primary}` ";
            foreach ($values as $row) {
                $value      = $row[$column];
                $bindings[] = $value;

                $setStr .= " when '{$row[$primary]}' then ? ";
            }
            $setStr .= ' end,';
        }
        // Remove the last character
        $setStr = substr($setStr, 0, -1);

        $ids    = array_column($values, $primary);
        $idsStr = implode(',', $ids);

        $sql = "update {$table} set {$setStr} where {$primary} in ({$idsStr})";
        return [$sql, $bindings];
    }
    /**
     * @param array $where 查询条件
     * @param string[] $options 可选项 ['orderByRaw'=> 'id asc', 'skip' => 15, 'take' => 5]
     * @return
     */
    public function optionWhere(array $where, array $options = [])
    {
        /** @var \Hyperf\Database\Model\Builder $model */
        $model = new static();

        if (! empty($where) && is_array($where)) {
            foreach ($where as $k => $v) {
                ## 一维数组
                if (! is_array($v)) {
                    $model = $model->where($k, $v);
                    continue;
                }

                ## 二维索引数组
                if (is_numeric($k)) {
                    $boolean = $v[3] ?? 'and';
                    if (count($v) == 2){
                        $model = $model->where($v[0], '=', $v[1], $boolean);
                        continue;
                    }
                    $v[1]    = mb_strtoupper($v[1]);

                    if (in_array($v[1], ['=', '!=', '<', '<=', '>', '>=', 'LIKE', 'NOT LIKE'])) {
                        $model = $model->where($v[0], $v[1], $v[2], $boolean);
                    } elseif ($v[1] == 'IN') {
                        $model = $model->whereIn($v[0], $v[2], $boolean);
                    } elseif ($v[1] == 'NOT IN') {
                        $model = $model->whereNotIn($v[0], $v[2], $boolean);
                    } elseif ($v[1] == 'RAW') {
                        $model = $model->whereRaw($v[0], $v[2], $boolean);
                    }
                } else {
                    ## 二维关联数组
                    $model = $model->whereIn($k, $v);
                }
            }
        }

        ## 排序
        isset($options['orderByRaw']) && $model = $model->orderByRaw($options['orderByRaw']);

        ## 限制集合
        isset($options['skip']) && $model = $model->skip($options['skip']);
        isset($options['take']) && $model = $model->take($options['take']);

        return $model;
    }
    /**
     * columnsFormat
     * @param array $data
     * @param bool $transToSnake
     * @param bool $isColumnFilter
     * @return array
     */
    protected function columnsFormat(array $value,  bool $isTransSnake = false, bool $isColumnFilter = false): array
    {
        //使用this->getField方法获取数据库字段.
        //根据获取的字段过滤$data中不存在于表内的字段数据
        $formatValue                     = [];
        $isColumnFilter && $tableColumns = array_flip($this->getField());
        foreach ($value as $field => $fieldValue) {
            ## 转snake
            $isTransSnake && $field = Str::snake($field);
            ## 过滤
            if ($isColumnFilter && ! isset($tableColumns[$field])) {
                continue;
            }
            $formatValue[$field] = $fieldValue;
        }
        return $formatValue;
    }

    /**
     * 定义一个获取字段的接口
     * @return array
     */
    abstract function getField(): array;

}